/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <baja/error.hpp>
#include <journal/views/directx/effect_service.h>
#include <journal/views/directx/util.hpp>
#include <baja/stream/stream.hpp>

namespace journal { namespace view {

// TODO: this api is stupid, i should have an effect object so i dont have to do a lookup all the time and just move
// these methods there
void effect_service::setEffectConstants(const effect_id id, const void* buffer)
{
	auto it = _map.find(id);
	BAJA_VERIFY(it != _map.end());
	effect& e = it->second;

	_d3d11DeviceContext->UpdateSubresource(
        e.constantBuffer.Get(),
        0,
        NULL,
        buffer,
        0,
        0
        );

	// TODO: always setting slot 0.  i think i can do this way more efficiently using different slots same for textures
    _d3d11DeviceContext->VSSetConstantBuffers(
        0,
        1,
        e.constantBuffer.GetAddressOf()
        );
}

void effect_service::setTexture(const std::shared_ptr<g::itexture>& texture)
{
    WRL::ComPtr<ID3D11ShaderResourceView> shaderResource;
    shaderResource = d::bajaInterfaceToDirect3D<g::itexture, ID3D11ShaderResourceView>(texture);

	_d3d11DeviceContext->PSSetShaderResources(0, 1, shaderResource.Get());
}

void effect_service::applyEffect(const effect_id id)
{
	auto it = _map.find(id);
	BAJA_VERIFY(it != _map.end());
	effect& e = it->second;

	_d3d11DeviceContext->VSSetShader(
        e.vertexShader.Get(),
        nullptr,
        0
        );

	_d3d11DeviceContext->PSSetShader(
        e.pixelShader.Get(),
        nullptr,
        0
        );
}

const void* effect_service::getShaderBytes(const effect_id id)
{
	auto it = _map.find(id);
	BAJA_VERIFY(it != _map.end());
	effect& e = it->second;

	return e.vertexShaderBytes.data();
}

const size_t effect_service::getShaderSize(const effect_id id)
{
	auto it = _map.find(id);
	BAJA_VERIFY(it != _map.end());
	effect& e = it->second;

	return e.vertexShaderBytes.size();
}

void effect_service::loadEffect(const effect_id id, const effect_description& desc)
{
	b::win32_result wr;

    auto it = _map.find(id);
    if (it != _map.end())
    {
		// already loaded
        return;
    }
        
    std::shared_ptr<b::imemory_stream> fileStream;
    fileStream = b::createMemoryStreamFromFile(desc.pixelShaderFile);

	effect e;

	wr = _d3d11Device->CreatePixelShader(
        fileStream->getBuffer(),
        fileStream->getBufferSize(),
        nullptr,
        e.pixelShader.GetAddressOf()
        );

    fileStream = b::createMemoryStreamFromFile(desc.vertexShaderFile);

	wr = _d3d11Device->CreateVertexShader(
        fileStream->getBuffer(),
        fileStream->getBufferSize(),
        nullptr,
        e.vertexShader.GetAddressOf()
        );

	// TODO: this is very inefficient... should just move the vector from the file stream
	e.vertexShaderBytes.resize(fileStream->getBufferSize());
	std::memcpy(e.vertexShaderBytes.data(), fileStream->getBuffer(), fileStream->getBufferSize());

	wr = _d3d11Device->CreateBuffer(
        &CD3D11_BUFFER_DESC(desc.constantBufferSize, D3D11_BIND_CONSTANT_BUFFER),
        nullptr,
        e.constantBuffer.GetAddressOf()
		);

	_map[id] = e;
}

void effect_service::initialize()
{
	_d3d11DeviceContext = getDeviceContext(_graphics);
	_d3d11Device = getDevice(_graphics);

	// load the effects

	effect_description desc;
	desc.constantBufferSize = sizeof(quad_constants);
	desc.pixelShaderFile = L"quad_pixel_shader.cso";
	desc.vertexShaderFile = L"quad_vertex_shader.cso";

	this->loadEffect(effect_id::quad, desc);
}

}} // namespace journal::view